<?php
/**
 * Created by PhpStorm.
 * User: longli
 * VX: isa1589518286
 * Date: 2021/01/25
 * Time: 13:58
 * @link http://www.lmterp.cn
 */

namespace app\common\service\logistics;

use app\common\model\ChannelOrders;
use app\common\model\Orders;
use app\common\model\OrdersReturn;
use app\common\service\BaseService;

require_once \Env::get("root_path") . 'extend/51track/Init.class.php';
/**
 * 51track 获取物流信息
 * Class Track51
 * @package app\common\service\logistics
 * @link https://www.51tracking.com/api-index
 */
class Track51 extends BaseService
{
    /**
     * 订单id
     * @var array
     */
    protected $ids;

    /**
     * 错误信息
     * @var array
     */
    protected $error = [];

    /**
     * 追踪号
     * @var array
     */
    protected $track = [];

    /**
     * 响应信息
     * @var array
     */
    protected $data = [];

    public function __construct($ids)
    {
        \APITracking\Api::setApiKey(config('param.51track_api_key'));
        $this->ids = is_array($ids) ? $ids : explode(',', $ids);
        if(count($this->ids) > 40)
        {
            throw new \RuntimeException("批量处理不能超过40条");
        }
    }

    /**
     * 退货物流订单信息
     * @param $orderId
     * @date 2021/03/08
     * @author longli
     */
    public function returnOrder()
    {
        $this->resetData();
        foreach(OrdersReturn::whereIn("rid", $this->ids)->select() as $returnOrder)
        {
            if(!$this->validateOrder($returnOrder)) continue;
            $this->track[] = [
                'tracking_number' => $returnOrder->track_num,
                'carrier_code'    => $returnOrder->channel->track_code,
            ];
        }
        $this->batch();
        $this->updateReturnOrder();
    }

    /**
     * 发货订单物流信息
     * @date 2021/01/25
     * @author longli
     */
    public function channelOrder()
    {
        $this->resetData();
        foreach(ChannelOrders::with(["channel"])->whereIn("ch_id", $this->ids)->select() as $chOrder)
        {
            if(!$this->validateOrder($chOrder)) continue;
            $this->track[] = [
                'tracking_number' => $chOrder->track_num,
                'carrier_code'    => $chOrder->channel->track_code,
            ];
        }
        $this->batch();
        $this->updateChannelOrder();
    }

    /**
     * 更新退货订单信息
     * @date 2021/03/08
     * @author longli
     */
    protected function updateReturnOrder()
    {
        foreach($this->getData() as $data)
        {
            // 更新退货订单物流状态
            if(!($returnOrder = OrdersReturn::get(['track_num' => trim($data['tracking_number'])]))) continue;
            $returnOrder->track_info = json_encode($data);
            $returnOrder->track_status = $this->mapping($data['status']);
            $returnOrder->save();
            if($returnOrder->getData('track_status') == ChannelOrders::TRACK_STATUS_RECV)
            {
                $returnOrder->order->send_status = $returnOrder->isReturnAll()
                    ? Orders::SEND_RETURN_ALL
                    : Orders::SEND_RETURN_PART;
                $returnOrder->order->save();
            }
        }
    }

    /**
     * 更新物流信息
     * @date 2021/01/25
     * @author longli
     */
    protected function updateChannelOrder()
    {
        foreach($this->getData() as $data)
        {
            // 更新订单物流状态
            if(!($chOrder = ChannelOrders::get(['track_num' => trim($data['tracking_number'])]))) continue;
            $order = Orders::get($chOrder->order_id); // 加载订单
            $chOrder->track_info = json_encode($data);
            if($order->getData('order_status') >= Orders::ORDER_SEND)
                $chOrder->track_status = $this->mapping($data['status']);
            $chOrder->save();

            // 更新订单状态
            if(in_array($chOrder->getData('track_status'),
                [ChannelOrders::TRACK_STATUS_NONE, ChannelOrders::TRACK_STATUS_NOMAPPING])) continue;
            $ts = $chOrder->getData('track_status');
            if(isset(ChannelOrders::$MAPPING_ORDER_SEND[$ts]))
            {
                if($order->getData('order_status') < Orders::ORDER_SEND)
                    $order->order_status = Orders::ORDER_SEND;
                $order->send_status = ChannelOrders::$MAPPING_ORDER_SEND[$ts];
                $order->save();
            }
        }
    }

    /**
     * 验证订单是否需要请求物流信息
     * @param $order
     * @return bool
     * @date 2021/01/25
     * @author longli
     */
    protected function validateOrder($order)
    {
        if(empty($order->channel) || $order->channel->getData('is_track') == ChannelOrders::IS_NO) return false;
        if(empty($order->channel->track_code))
        {
            $this->error[] = "渠道【{$order->channel->channel_name}】未配置，对接信息";
            return false;
        }
        $table = $order->getTable();
        $pk = $order->getPk();
        if(empty($order->track_num))
        {
            $this->error[] = "数据表【{$table}】id【{$order->$pk}】追踪号不存在";
            return false;
        }
        $time = strtotime($order->create_time);
        $threeMonth = strtotime("-3month");
        if($time < $threeMonth)
        {
            $this->error[] = "数据表【{$table}】id【{$order->$pk}】追踪号【{$order->track_num}】已超过三个月";
            return false;
        }

        if(!in_array($order->getData('track_status'), [
            ChannelOrders::TRACK_STATUS_WAIT,
            ChannelOrders::TRACK_STATUS_OUT,
            ChannelOrders::TRACK_STATUS_NONE,
            ChannelOrders::TRACK_STATUS_SHIPPING,
            ChannelOrders::TRACK_STATUS_PICKUP,
        ])) return false;
        return true;
    }

    /**
     * 批量获取
     * @param array 追踪号
     * @return bool
     * @date 2021/01/25
     * @author longli
     */
    protected function batch()
    {
        if(empty($this->track))
        {
            $this->error[] = "没有要获取物流信息的订单";
            return false;
        }
        $response = \APITracking\Batch::create($this->track);
        if(!$this->validateResponse($response)) return false;
        foreach($this->track as $item)
        {
            $this->get($item['tracking_number'], $item['carrier_code']);
        }
        return true;
    }

    /**
     * 获取单个物流信息
     * @param string $track 追踪号
     * @param string $channelCode 渠道代码
     * @return bool
     * @date 2021/01/25
     * @author longli
     */
    protected function get($track, $channelCode)
    {
        $temp = $attempts = config('api_attempts');
        do
        {
            $response = \APITracking\Single::get($track, $channelCode);
            if(!$this->validateResponse($response)) return false;
            $this->data[] = $response['data'];
            if($attempts < $temp) sleep(1);
        }while(trim($response['data']['status']) == 'pending' && $attempts--);
        return true;
    }

    /**
     * 验证响应信息
     * @param string|array $response 响应信息
     * @return bool
     * @date 2021/01/25
     * @author longli
     */
    protected function validateResponse(& $response)
    {
        if(empty($response))
        {
            $this->error[] = "api 请求不通";
            return false;
        }
        if(is_string($response)) $response = json_decode($response, true);
        if(!in_array($response['meta']['code'], [200, 201, 4016]))
        {
            $this->error[] = "Api 请求失败，状态码【{$response['meta']['code']}】错误信息【{$response['meta']['message']}】";
            return false;
        }
        return true;
    }

    /**
     * 映射状态信息
     * @param string $s 物流平台状态
     * @return int
     * @date 2021/01/25
     * @author longli
     */
    protected function mapping($s)
    {
        $ts = [
            'pending'  => ChannelOrders::TRACK_STATUS_WAIT, // 待揽收
            'notfound' => ChannelOrders::TRACK_STATUS_NONE, // 没有物流信息
            'transit' => ChannelOrders::TRACK_STATUS_SHIPPING, // 运输中
            'pickup' => ChannelOrders::TRACK_STATUS_PICKUP, // 派送中
            'delivered' => ChannelOrders::TRACK_STATUS_RECV, // 已签收
            'expired' => ChannelOrders::TRACK_STATUS_TIMEOUT, // 运输超时
            'undelivered' => ChannelOrders::TRACK_STATUS_UNRECV, // 派送失败
            'exception' => ChannelOrders::TRACK_STATUS_EXCEPTION, // 物流异常
        ];
        return isset($ts[$s]) ? $ts[$s] : ChannelOrders::TRACK_STATUS_NOMAPPING;
    }

    public function resetData()
    {
        $this->track = $this->data = [];
    }

    /**
     * @return array
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * @return array
     */
    public function getError()
    {
        return $this->error;
    }
}